package main

import (
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

const (
	DEFAULT_VERSION_EXP string = `\$_cur_version = '{VERSION}';`
	PACKAGE_TYPE        string = "package"
	PATCH_TYPE          string = "patch"
	VERSION_MARK        string = "{VERSION}"
)

type Project struct {
	// project name
	Name string `xml:"name,attr"`
	// destination dir
	Dest string
	// boot file path
	Bootstrap string
	// version string
	VersionExp string ""

	PatchLevel int
}

type Deploy struct {
	Server  string
	Project []Project
}

var deployConf Deploy

func main() {
	wd, _ := os.Getwd()
	path := wd + "/deploy.client.conf.xml"
	load_conf(path)
	log.Print(deployConf)

	log.Println("hello,world")
	http.HandleFunc("/deploy/upload/", http_upload)
	http.HandleFunc("/deploy/_upload/", http_do_upload)
	http.ListenAndServe(":9999", nil)
}

func load_conf(path string) {
	deployConf = Deploy{}

	content, err := ioutil.ReadFile(path)
	if err != nil {
		panic("load config xml failed:" + err.Error())
	}
	err = xml.Unmarshal([]byte(content), &deployConf)
	if err != nil {
		log.Printf("error:%v", err)
		return
	}
	for k, prj := range deployConf.Project {
		if prj.VersionExp == "" {
			deployConf.Project[k].VersionExp = DEFAULT_VERSION_EXP
		}
	}
}

func get_project(prjName string) (ok bool, project Project) {
	log.Print(deployConf)
	ok = false
	if len(deployConf.Project) == 0 {
		return
	}

	for _, prj := range deployConf.Project {
		log.Print(prjName, ":", prj.Name)
		if prj.Name == prjName {
			return true, prj
		}
	}
	return
}
func http_upload(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprint(rw, `
<!doctype html>
<html>
	<head>
		<title>upload</title>
	</head>
	<body>
		<form method="post" action="/deploy/_upload/" enctype="multipart/form-data">
			<input type="hidden" name="type" value="package"/>
			<input type="hidden" name="new" value="20140318"/>
			<input type="hidden" name="project" value="jiehun"/>
			<input type="file" name="pack"/>
			<input type="submit" value="upload"/>
		</form>
	</body>
</html>
	`)
}

func http_do_upload(rw http.ResponseWriter, req *http.Request) {
	req.ParseMultipartForm(32 << 20)

	uploadType := req.FormValue("type")
	prjName := req.FormValue("project")
	newTag := req.FormValue("new")
	oldTag := req.FormValue("old")

	log.Println(uploadType)
	log.Println(prjName)
	log.Println(newTag)

	if uploadType != PACKAGE_TYPE && uploadType != PATCH_TYPE {
		http.Error(rw, "type is not supplied", 500)
		return
	}
	if uploadType == PATCH_TYPE && oldTag == "" {
		http.Error(rw, "old tag is not supplied", 500)
		return
	}
	log.Println(prjName == "" || newTag == "")
	if prjName == "" || newTag == "" {
		http.Error(rw, "project name/new tag is not supplied", 500)
		return
	}
	file, header, err := req.FormFile("pack")
	if err != nil {
		http.Error(rw, err.Error(), 500)
		return
	}
	defer file.Close()

	var project Project
	var ok bool
	if ok, project = get_project(prjName); !ok {
		http.Error(rw, "project is not defined", 500)
		return
	}

	path := project.Dest + "/" + header.Filename
	f, err := os.Create(path)
	defer f.Close()
	io.Copy(f, file)
	os.Chdir(filepath.Dir(path))
	cmd := exec.Command("/bin/tar", "xvfz", path)
	log.Println(cmd)
	cmd.Run()

	if uploadType == PACKAGE_TYPE {
		if project.Bootstrap != "" && project.VersionExp != "" {
			handle_bootstrap(project.Bootstrap, project.VersionExp, newTag)
		}
	} else if uploadType == PATCH_TYPE {
		oldDir := project.Dest + "/" + oldTag
		newDir := project.Dest + "/" + newTag
		exec.Command("cp", "-r", oldDir, newDir).Run()
		os.Chdir(newDir)
		cmd := exec.Command("patch", "-p", string(project.PatchLevel), "<"+path)
		log.Println(cmd)
		cmd.Run()
	}
}

/**
 * handle the bootstrap file
 * @param  {[type]} path string
 * @param  {[type]} exp  string
 * @return {[type]}
 */
func handle_bootstrap(path, exp, version string) {
	orig := strings.Replace(exp, VERSION_MARK, ".*", 1)
	dest := strings.Replace(exp, VERSION_MARK, version, 1)
	cmd := exec.Command("/bin/sed", "-i", fmt.Sprintf("-e \"s/%s/%s/\"", orig, dest), path)
	log.Println(cmd)
	cmd.Run()
}
